#ifndef WOLF_EXCEPTION_HPP
#define WOLF_EXCEPTION_HPP

#include <SDL.h>
#include <exception>
#include <sstream>
using namespace std;

namespace wolf
{
/**
 * @addtogroup errorsMod
 * @{
 */
/**
 * @brief The basic error class of the library
 *
 * It is provided to offer compatibility with the exception classes of std library.
 **/
class Error: public exception
{
public:
	/**
	 * @brief A small text explaining the error
	 *
	 * The text should be human readable. Auto error correcting functions
	 *  should use other class members.
	 **/
	const char* what() const throw () = 0;

//	virtual ~Error(){}
};

/**
 * @brief Throw whenever occurs a SDL error
 *
 *
 **/
class SdlError: public Error
{
public:
	/**
	 * @brief Create a SdlError with the given reason
	 *
	 * @param reason The return value of SDL_GetError();
	 **/
	SdlError(const char *reason_ = SDL_GetError()) throw ()
	{
		reason = reason_;
	}
	const char* what() const throw ()
	{
		return reason;
	}
private:
	const char * reason;
};

/**
 * @brief Throw when occurs a error on initialization
 *
 * It has attributes to help to identify what went wrong.
 *  Use the hasFrameRate() and hasScreen() to check what is
 *  not initialized.
 **/
class EngineError: public Error
{
public:
	/**
	 * @brief Creates a engine error
	 *
	 * The Params identify what is wrong.
	 *
	 * @param hasFrameRate Pass it if the frameRate is defined;
	 * @param hasScreen Pass it if the Screen is NULL;
	 **/
	EngineError(bool hasFrameRate_, bool hasScreen_)
	{
		_hasFrameRate = hasFrameRate_;
		_hasScreen = hasScreen_;
	}

	/**
	 * @brief Returns if the frame rate is defined
	 *
	 *
	 **/
	bool hasFrameRate()
	{
		return _hasFrameRate;
	}

	/**
	 * @brief Returns if the screen is initialized
	 *
	 *
	 **/
	bool hasScreen()
	{
		return _hasScreen;
	}

	const char* what() const throw ()
	{
		stringstream out;
		//( stringstream() << "Missing: \n" << ( _frameRate ? "" : "\tFrameRate definition\n" ) << ( _screen ? "" :"\tScreen definition" ) )._str()
		out << "Missing: \n" << (_hasFrameRate ? "" : "\tFrameRate definition\n")
				<< (_hasScreen ? "" : "\tScreen definition\n");
		return out.str().c_str();
	}
private:
	bool _hasFrameRate, _hasScreen;
};

/**
 * @brief Occurs when singleton rules are violated
 *
 *
 **/
class SingletonError: public Error
{
public:
	SingletonError(const char *reason_)
	{
		reason = reason_;
	}

	const char* what() const throw ()
	{
		return reason;
	}
private:
	const char *reason;
};

class AssertError: public Error {
public:
	AssertError(const string &exp_, const string &file_, int line_):
		expression(exp_), file(file_)
	{
		line = line_;
	}

    const char *what() const throw ()
	{
    	stringstream s;
    	s << "Assertion Error on file '" << file << "#" << line <<"': '" << expression << "' is invalid";
    	return s.str().c_str();
	}

    string getExpression() const
    {
        return expression;
    }

    string getFile() const
    {
        return file;
    }

    int getLine() const
    {
        return line;
    }


    virtual ~AssertError() throw()
    {
    	//Do nothing
    }

private:
    string expression;
    string file;
    int line;
};
/**
 * @}
 */
}

#ifdef WOLF_DEBUG
#define WOLF_ASSERT(x) ((x)?(void)0:( throw AssertError(#x, __FILE__, __LINE__)) )
#else
#define WOLF_ASSERT(x) (void)0
#endif

#endif // WOLF_EXCEPTION_HPP
